package org.xingole.jgit.lib;

import org.xingole.jgit.utils.NetworkByteHandler;

/**
 * <p>A mutable SHA-1 abstraction.</p>
 *
 * <p>It can be used to copy an ObjectId into a mutable buffer, or convert an ObjectId from raw binary representation.</p>
 *
 * <p>Em... Maybe we can view the ObjectId as the String and the MutableObjectId as the StringBuilder. That's the
 * core of the immutable and mutable properties.</p>
 */
public class MutableObjectId  extends AnyObjectId{
    // Constructors

    /**
     * <p>Empty constructor. Initialize object with default(zero) value.</p>
     *
     * <p>ha! We use the static statement in the ObjectId class to simulate the same purpose. Here we just use the
     * empty constructor to achieve the same goal, because those two class has different design principle. we can
     * also think of the distinguish as a type of the design pattern.</p>
     */
    public MutableObjectId() {
        super();
    }

    /**
     * Copying constructor.
     * @param src - original entry, to copy id from
     */
    MutableObjectId(MutableObjectId src) {
        // we can call the common method of the class to complete the work.
        // The specific method call the common  method to implement the hierarchy method calling procedure.
        fromObjectId(src);
    }

    // Modifying Methods

    /**
     * Set any byte in the id.
     *
     * <p>Yeah! It's the method that just is the setter for the class, with the bytes (such as w0...w4) as the
     * property.</p>
     *
     * @param index - index of the byte to set in the raw form of the ObjectId. Must be in the range [0,
     *              Constants.OBJECT_ID_LENGTH]. (ya! quickly to analyze the constants and create it.)
     * @param value - the value of the specified byte at index. Values are unsigned and thus are in the range
     *              [0, 255] rather than the signed byte range of [-128, 127]. (that is to say we should consider
     *              the unsigned and signed converting problem - negative value should be viewed as the corresponding
     *              positive value)
     */
    public void setByte(int index, int value) {
        switch (index >> 2) {
            case 0:
                w0 = set(w0, index & 3, value);
                break;
            case 1:
                w1 = set(w1, index & 3, value);
                break;
            case 2:
                w2 = set(w2, index & 3, value);
                break;
            case 3:
                w3 = set(w3, index &  3, value);
                break;
            case 4:
                w4 = set(w4, index & 3, value);
                break;
            default:
                throw new ArrayIndexOutOfBoundsException();
        }
    }

    private static int set(int w, int index, int value) {
        value &= 0xff;  // extract the lower byte of the value

        /*
         *  Implementation notes:
         *
         *       index:          0     1     2     3
         *  SHA-1 string: low   BP                    high       RIGHT TO LEFT in display but LEFT TO RIGHT in analysing
         *                 |----|----------------------|
         *                     ws3    ws2   ws1   ws0
         *                 -----|-------w---------|-----
         * RIGHT TO LEFT       MSB               LSB
         *
         * Step:
         *    1. reset the specific location to zero using the AND operation;
         *    2. shift the value byte to the specific location using the RIGHT SHIFT operation;
         *    3. concat the above two step result using the OR operation.
         */

        switch (index) {
            case 0:
                return (w & 0x00_ff_ff_ff) | (value << 24);
            case 1:
                return (w & 0xff_00_ff_ff) | (value << 16);
            case 2:
                return (w & 0xff_ff_00_ff) | (value << 8);
            case 3:
                return (w & 0xff_ff_ff_00) | value;
            default:  // Warning: happening the out of bounds exception !!!
                throw new ArrayIndexOutOfBoundsException();
        }
    }

    /**
     * Convert an ObjectId from binary representation expressed in integers.
     *
     * @param w0
     * @param w1
     * @param w2
     * @param w3
     * @param w4
     */
    public void set(int w0, int w1, int w2, int w3, int w4) {
        this.w0 = w0;
        this.w1 = w1;
        this.w2 = w2;
        this.w3 = w3;
        this.w4 = w4;
    }
    /**
     * Make this id match ObjectId.zeroId().
     */
    public void clear() {
        w0 = 0;
        w1 = 0;
        w2 = 0;
        w3 = 0;
        w4 = 0;
    }

    // Converting Methods

    /**
     * Copy an ObjectId into this mutable buffer.
     *
     * <p>With the purpose of the class to copy any object id to mutable buffer namely mutable object id instance,
     * we should obviously create the method for multi-purpose.</p>
     *
     * @param src - the source id to copy from.
     */
    public void fromObjectId(AnyObjectId src) {
        this.w0 = src.w0;
        this.w1 = src.w1;
        this.w2 = src.w2;
        this.w3 = src.w3;
        this.w4 = src.w4;
    }

    /**
     * Convert an ObjectId from raw binary representation.
     *
     * @param bs - the raw byte buffer to read from. At least 20 bytes after p must be available within this byte
     *           array.
     * @param p - position to read the first byte of data from.
     */
    public void fromRaw(final byte[] bs, final int p) {
        /*
         * Implementation Notes:
         *
         *   Adapter write the data into the buffer
         *      ||
         * Raw byte buffer:                         p  reading start
         *                  ------------------------|---------------------------------------      read
         *                  | space_i |  .... | space_k | ....    |space_k19| ... | space_n|    ========>>> Java program
         *                  -------------------------------------------|--------------------
         *                                                           reading end
         *  every space occupy a byte size (8 bits).
         *
         *  Some parts in the raw byte buffer are used to save sha-1 data.
         */
        w0 = NetworkByteHandler.decodeInt32(bs, p);
        w1 = NetworkByteHandler.decodeInt32(bs, p + 4);
        w2 = NetworkByteHandler.decodeInt32(bs, p + 8);
        w3 = NetworkByteHandler.decodeInt32(bs, p + 12);
        w4 = NetworkByteHandler.decodeInt32(bs, p + 16);
    }

    // Common methods for user to convert an objectId from raw binary representation

    /**
     * Convert an ObjectId from raw binary representation.
     *
     * @param bs - the raw byte buffer to read from. At least 20 bytes must be available within this byte array.
     */
    public void fromRaw(final byte[] bs) {
        fromRaw(bs, 0);
    }

    /**
     * Convert an ObjectId from binary representation expressed in integers.
     *
     * @param ints - the raw int buffer to read from. At least 5 integers after p must be available within this integers
     *             array.
     * @param p - position to read the first integer of data from.
     */
   public void fromRaw(final int[] ints, final int p) {
        w0 = ints[p];
        w1 = ints[p + 1];
        w2 = ints[p + 2];
        w3 = ints[p + 3];
        w4 = ints[p + 4];
   }

    /**
     * Convert an ObjectId from binary representation expressed in integers.
     *
     * @param ints - the raw int buffer to read from. At least 5 integers must be available within this integers array.
     */
   public void fromRaw(final int[] ints) {
       fromRaw(ints, 0);
   }
}
